M2.855 · Modelos avanzados de minería de datos · PEC4
2020-2 · Máster universitario en Ciencia de datos (Data science)
Estudios de Informática, Multimedia y Telecomunicación
Está práctica está dividida en dos partes:
Francisco Javier Melchor González
Para esta PEC se deben usar únicamente las librerías que se importan a continuación. Si se desea usar otra librería hay que consultarlo con el tutor/tutora del aula.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pickle
from sklearn import datasets
from sklearn.ensemble import GradientBoostingClassifier, RandomForestClassifier
from sklearn.linear_model import LinearRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import cross_val_score, train_test_split, validation_curve
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
from sklearn.preprocessing import OneHotEncoder
from sklearn.svm import SVC, SVR
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
%matplotlib inline
En la primera parte de este ejercicio analizaremos los resultados de distintos modelos de regresión aplicados a tres conjuntos de datos sintéticos con los que veremos sus fortalezas y debilidades.
En la segunda parte aplicarermos el modelo que creamos más oportuno a un problema de la vida real.
Generaremos tres conjuntos de datos con un único atributo descriptivo que utilizaremos para predecir el valor de la variable dependiente, pudiendo así visualizar en dos dimensiones el comportamiento de cada modelo.
Por otro lado, en los problemas de regresión podemos encontrar dos tipos de problemática diferente: interpolación (la predicción se realizará en base a atributos descriptivos dentro de un intervalo conocido) y extrapolación (alguno de sus atributos descriptivos toma valores fuera del rango de valores visto por el algoritmo durante la etapa de aprendizaje).
Como sólo dispondremos de un atributo descriptivo, pondremos un umbral en dicho atributo para diferenciar lo que sería interpolación (lo marcaremos en azul) de lo que sería extrapolación (en rojo). La gran diferencia entre estos dos subconjuntos de datos es que mientras parte de los datos que interpolaremos los utilizaremos para entrenar, ningún dato de los que extrapolaremos los utilizaremos en la etapa de aprendizaje.
En siguiente código genera y muestra los tres conjuntos de datos:
# Cada variable (linear, sinusoidal y combined) será una tupla con cinco posiciones:
# el nombre del dataset y los dos valores X e y tanto para casos de interpolación como de extrapolación.
X, y = datasets.make_regression(n_features=1, n_samples=100, bias=9, noise=9, random_state=0)
X, y = zip(*sorted(zip(X,y)))
X = np.array(X); y = np.array(y)
data_linear = ('linear', X[:75], y[:75], X[-25:], y[-25:])
X, y = datasets.make_regression(n_features=1, n_samples=100, bias=5, noise=5, random_state=0)
y = (50*np.sin(5*X)).ravel()
X, y = zip(*sorted(zip(X,y)))
X = np.array(X); y = np.array(y)
data_sinusoidal = ('sinusoidal', X[:75], y[:75], X[-25:], y[-25:])
X, y = datasets.make_regression(n_features=1, n_samples=100, bias=5, noise=5, random_state=0)
y += (50*np.sin(5*X)).ravel()
X, y = zip(*sorted(zip(X,y)))
X = np.array(X); y = np.array(y)
data_combined = ('combined', X[:75], y[:75], X[-25:], y[-25:])
# Guardamos todos los datasets en una tupla.
datasets_tuple = (data_linear, data_sinusoidal, data_combined)
# Y mostramos su distribución bidimensional
fig, axis = plt.subplots(1, 3, figsize=(15,5))
for dataset, ax in zip(datasets_tuple, axis):
ax.scatter(dataset[1], dataset[2], alpha=0.5, color='b', s=50, marker='o')
ax.scatter(dataset[3], dataset[4], alpha=0.5, color='r', s=50, marker='o')
ax.set_title('Dataset {}'.format(dataset[0]))
plt.tight_layout()
Con el objetivo de mostrar las capacidades de cada una de las técnicas de regresión sobre los tres conjuntos de datos generados, se facilita la siguient función que, dado un modelo, lo entrena para luego mostrar los resultados obtenidos, tanto en los casos de interpolación como de extrapolación.
def plot_regressor_performance(reg):
fig, axis = plt.subplots(1, 3, figsize=(15,5))
for dataset, ax in zip(datasets_tuple, axis):
reg.fit(dataset[1], dataset[2])
print(f'Dataset {dataset[0]}, '
f'score interpolation: {reg.score(dataset[1], dataset[2])}, '
f'score extrapolation: {reg.score(dataset[3], dataset[4])}')
X_interpolation = np.linspace(
np.min(dataset[1]), np.mean(np.array([np.max(dataset[1]), np.min(dataset[3])])), 7500
).reshape(-1, 1)
X_extrapolation = np.linspace(
np.mean(np.array([np.max(dataset[1]), np.min(dataset[3])])), np.max(dataset[3]), 2500
).reshape(-1, 1)
ax.plot(X_interpolation, reg.predict(X_interpolation), color='b', linewidth=3)
ax.scatter(dataset[1], dataset[2], alpha=0.25, color='b', s=100, marker='o')
ax.plot(X_extrapolation, reg.predict(X_extrapolation), color='r', linewidth=3)
ax.scatter(dataset[3], dataset[4], alpha=0.25, s=100, color='r')
ax.set_title(f'Dataset {dataset[0]}')
plt.tight_layout()
Regresión Lineal
plot_regressor_performance(LinearRegression())
Dataset linear, score interpolation: 0.9141115687129225, score extrapolation: 0.7480861816554351 Dataset sinusoidal, score interpolation: 0.09687072757724613, score extrapolation: -1.4489455436255243 Dataset combined, score interpolation: 0.585078624067839, score extrapolation: -0.5169205593597506
KNN
plot_regressor_performance(KNeighborsRegressor())
Dataset linear, score interpolation: 0.9144613698009034, score extrapolation: -2.7151904692147992 Dataset sinusoidal, score interpolation: 0.9472527290757954, score extrapolation: -0.1811960157483905 Dataset combined, score interpolation: 0.9741670334014625, score extrapolation: -0.1547222080136894
SVM
plot_regressor_performance(SVR())
Dataset linear, score interpolation: 0.4971483839350117, score extrapolation: -12.38567142640009 Dataset sinusoidal, score interpolation: 0.10289729257219382, score extrapolation: -0.7054925398584895 Dataset combined, score interpolation: 0.22424838318873674, score extrapolation: -1.279542986466057
Árbol de decisión
plot_regressor_performance(DecisionTreeRegressor())
Dataset linear, score interpolation: 1.0, score extrapolation: -1.3458417109611087 Dataset sinusoidal, score interpolation: 1.0, score extrapolation: -0.15418002495961636 Dataset combined, score interpolation: 1.0, score extrapolation: -0.7313473636504104
El modelo de regresión lineal se comporta adecuadamente sólo con el Dataset linear, tanto en la problemática de interpolación como de extrapolación, tal y como se puede ver a través de los colores azul y rojo respectivamente. Sin embargo, con el Dataset sinusoidal y con el Dataset combined, el modelo de regresión lineal no logra ajustarse adecuadamente a los datos, lo cual era de esperar ya que este sólo es adecuado utilizarlo para aquellos datasets cuyo conjunto de datos es lineal.
Por otro lado, el modelo de regresión lineal extrapola modelos lineales (crecientes en este caso) por la tendencia del modelo a crear modelos lineales, lo que hace que este, tal y como se ha comentado anteriormente, sólo se adapte adecuadamente al Dataset linear
El modelo de KNN logra comportarse adecuadamente a los diferentes datasets, sobre todo al Dataset sinusoidal y al Dataset combined, cuando está resolviendo la problemática de interpolación (color azul), sin embargo, para los datos de extrapolación no logra adaptarse bien a ninguno de los datasets.
Por otro lado, este modelo extrapola modelos constantes, es decir, sin ninguna variación independientemente de los datos a predecir, pues como se puede ver, los fragmentos en rojos son constantes para todos los datasets.
El modelo SVM no se comporta correctamente para ninguno de los datasets, tanto para la problemática de interpolación como para la de extrapolación.
Por otro lado, por lo general, como se puede ver, los modelos que extrapola este modelo son sinusoidales.
De manera similar al modelo KNN, el modelo del Árbol de decisión logra adaptarse correctamente a los 3 datasets, pero únicamente cuando este está resolviendo la problemática de interpolación, cuando este tiene que resolver la problemática de extrapolación, al igual que el modelo KNN, extrapola modelos constantes.
Por lo general los modelos no se comportan adecuadamente para la problemática de extrapolación, excepto la regresión lineal, que logra adaptarse correctamente al Dataset linear incluso para el problema de extrapolación, pero esto es debido funcionamiento del modelo de regresión, pues este genera básicamente una ecuación lineal que se adapta lo máximo posible a los datos de entrenamiento dados y dicha ecuación resultante es utilizada posteriormente para realizar las diferentes predicciones, por lo que si el problema de extrapolación son datos que presentan una tendencia similar a la que presentan los datos con los que el modelo ha sido entrenado, el modelo logrará extrapolar correctamente, tal y como ocurre en este caso con el Dataset linear.
Señalar que esto no quiere decir que los modelos utilizados no logren extrapolar correctamente, sino que hemos utilizado los parámetros por defecto, si adaptasemos los modelos a los datos utilizados seguro que los resultados obtenidos para la problemática de extrapolación sería mucho mejor.
En este caso práctico utilizaremos un conjunto de datos público de precios de vivienda en Taiwán con el objetivo de encontrar las mejores ofertas del mercado. La descripción completa del conjunto de datos la podemos encontrar en el siguiente enlace: https://archive.ics.uci.edu/ml/datasets/Real+estate+valuation+data+set
Procedamos a cargar los datos en un DataFrame de pandas para su posterior uso:
dataset = pd.read_excel('../data/Real estate valuation data set.xlsx', engine='openpyxl')
dataset.head()
| No | X1 transaction date | X2 house age | X3 distance to the nearest MRT station | X4 number of convenience stores | X5 latitude | X6 longitude | Y house price of unit area | |
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2012.916667 | 32.0 | 84.87882 | 10 | 24.98298 | 121.54024 | 37.9 |
| 1 | 2 | 2012.916667 | 19.5 | 306.59470 | 9 | 24.98034 | 121.53951 | 42.2 |
| 2 | 3 | 2013.583333 | 13.3 | 561.98450 | 5 | 24.98746 | 121.54391 | 47.3 |
| 3 | 4 | 2013.500000 | 13.3 | 561.98450 | 5 | 24.98746 | 121.54391 | 54.8 |
| 4 | 5 | 2012.833333 | 5.0 | 390.56840 | 5 | 24.97937 | 121.54245 | 43.1 |
La primera columna ("No") es un identificador numérico de la muestra. Las columnas "Xn ..." son los distintos atributos descriptivos. Finalmente, la columna "Y ..." es el valor objetivo o precio de la vivienda.
Extraemos del DataFrame la variable objetivo y los atributos descriptivos que vayamos a utilizar posteriormente en el modelo predictivo:
X = dataset[[
'X2 house age',
'X3 distance to the nearest MRT station',
'X4 number of convenience stores',
'X5 latitude',
'X6 longitude'
]]
y = dataset['Y house price of unit area']
X.head()
| X2 house age | X3 distance to the nearest MRT station | X4 number of convenience stores | X5 latitude | X6 longitude | |
|---|---|---|---|---|---|
| 0 | 32.0 | 84.87882 | 10 | 24.98298 | 121.54024 |
| 1 | 19.5 | 306.59470 | 9 | 24.98034 | 121.53951 |
| 2 | 13.3 | 561.98450 | 5 | 24.98746 | 121.54391 |
| 3 | 13.3 | 561.98450 | 5 | 24.98746 | 121.54391 |
| 4 | 5.0 | 390.56840 | 5 | 24.97937 | 121.54245 |
for col in X:
plt.hist(X[col])
plt.xlabel(col)
plt.show()
for col in X:
plt.scatter(x=X[col],y=y)
plt.ylabel('House Price')
plt.xlabel(col)
plt.show()
latitud = X['X5 latitude']
longitud = X['X6 longitude']
import plotly.express as px
fig = px.density_mapbox(dataset, lat='X5 latitude', lon='X6 longitude', z='Y house price of unit area', radius=4,
center=dict(lat=24.97, lon=121.54), zoom=11,
mapbox_style="stamen-terrain")
fig.show()
Por un lado, podemos deducir a través de los histogramas que hay variación en las diferentes variables numéricas independientes, por lo que merece la pena, a priori, ver como se correlacionan todas ellas con la variable objetivo.
Por otro lado, a través de las gráficas de dispersión, podemos ver que las variables que más correlación presentan con la variable objetivo son:
"distance to the nearest MRT station", pues podemos ver como el precio aumenta a medida que la distancia es menor y viceversa, como el precio disminuye a mediada que la distancia es mayor.
"latitude" y "longitude", es decir, la localización. Podemos ver como aumenta el precio para algunos rangos de valores concretos, y además, podemos corroborarlo con el mapa de calor generado, pues podemos ver como algunas zonas incremente bastante al precio frente a otras
Por otro lado, las variables "house age" y "number of convenience stores", no parecen presentar una variación clara en el precio de la vivienda (variable objetivo) dependiendo de los diferentes valores que toman las mismas, sobre todo en el caso de la variable "number of convenience stores", por lo que a priori, no parece que estas variables estén correlacionadas con la variable objetivo.
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=24)
decissionTree_mod = DecisionTreeRegressor().fit(X_train,y_train)
La razón por la que he elegido la técnica DecisionTreeRegressor, es porque es la técnica que ha resultado dar mejores resultados en el apartado 1.1 para los diferentes conjuntos de datos con los que se ha probado,tanto lineal como no lineal.
En este caso concreto, nos conviene que se adapte correctamente a tipos de datos no lineales, ya que como podemos ver en las gráficas de puntos generadas, las relaciones de la variable objetivo con las diferentes variables, no son lineales, sino que el valor de la variable objetivo va variando de menos a más en algunos intervalos, sobre todo en el caso de las variables "latitude" y "longitude". La relación con "distance to nearest MRT station" sin embargo, presenta menos variaciones y se trata de una correlación inversa (diminuye el precio a medida que aumenta la distancia), aunque más que linealmente, esta disminuye de manera exponencial.
y_predict = decissionTree_mod.predict(X_test)
error = y_predict - y_test
plt.scatter(x=y_test,y=error)
plt.ylabel('Residual Error')
plt.xlabel('True Values')
plt.show()
plt.scatter(x=y_predict,y=error)
plt.ylabel('Residual Error')
plt.xlabel('Predicted Values')
plt.show()
En este ejercicio utilizaremos un subconjunto de 5.000 imágenes provenientes del corpus EMNIST, constituido por carácteres y dígitos escritos a mano . Dicho subconjunto está formado por 1.000 imágenes para cada una de las 5 clases diferentes ("A", "B", "C", "D" y "E").
La primera parte de este ejercicio abordará la combinación de clasificadores en paralelo mediante las tecnicas de Bagging y Boosting.
Mientras que la segunda parte pretende mejorar los resultados aplicando tecnicas de combinación secuencial de clasificadores: Stacking y Cascading.
Para empezar, vamos a visualizar el dataset. Las imágenes tienen una resolución de 28x28 píxeles en escala de grises, por lo que se pueden representar utilizando un vector de 784 posiciones. El siguiente código cargará las 5.000 imágenes en la variable images y las correspondientes etiquetas (en forma numérica) en la variable labels. Podemos comprobar que la carga ha sido correcta obteniendo las dimensiones de estas dos variables.
with open("data.pickle", "rb") as f:
data = pickle.load(f)
images = data["images"]
labels = data["labels"]
n_classes = 5
labels_text = ["A", "B", "C", "D", "E"]
print("Dimensiones del vector de imágenes: {}".format(images.shape))
print("Dimensiones del vector de etiquetas: {}".format(labels.shape))
Dimensiones del vector de imágenes: (5000, 784) Dimensiones del vector de etiquetas: (5000,)
Con el siguiente código podemos ver un ejemplo de imagen de cada una de las clases. Para ello reajustamos el vector de 784 dimensiones que representa cada imagen en una matriz de tamaño 28x28 y la transponemos para mostrarla:
fig, ax = plt.subplots(1, n_classes, figsize=(10,10))
idxs = [np.where(labels == i)[0] for i in range(n_classes)]
for i in range(n_classes):
k = np.random.choice(idxs[i])
ax[i].imshow(images[k].reshape(28, 28).transpose(), cmap="gray")
ax[i].set_title("{}".format(labels_text[i]))
Para poder probar varios modelos, primero vamos a dividir el dataset en train y test.
La división con la función train_test_split es aleatoria , pero para que todos obtengáis los mismos resultados y poder comentar dudas por el foro, fijaremos la seed para obtener los mismos datasets de train y test. El split tendrá en cuenta que los dos conjuntos tengan el mismo número de ejemplos para cada una de las clases gracias al parámetro stratify=labels.
Como en la segunda parte de este ejercicio trataremos stacking y cascading, y ambos se aplican sobre el conjunto de test, haremos un split del 50% para tener un poco más de base al aplicar estas dos técnicas.
myseed = 13
X_train, X_test, y_train, y_test = train_test_split(images, labels, test_size=0.5,
random_state=myseed,
stratify=labels)
clf_DecisionTree = DecisionTreeClassifier(max_depth=5)
def print_cv_results (cv_estimate):
print('Mean performance metric = %4.3f' % np.mean(cv_estimate))
print('SDT of the metric = %4.3f' % np.std(cv_estimate))
print('Outcomes by cv fold')
for i, x in enumerate(cv_estimate):
print('Fold %2d %4.3f' % (i+1, x))
print_cv_results (cross_val_score(clf_DecisionTree, X_train, y_train))
Mean performance metric = 0.792 SDT of the metric = 0.020 Outcomes by cv fold Fold 1 0.770 Fold 2 0.818 Fold 3 0.808 Fold 4 0.798 Fold 5 0.768
clf_DecisionTree.fit(X_train,y_train)
y_pred_clf_DecisionTree = clf_DecisionTree.predict(X_test)
accuracy_score(y_true=y_test, y_pred=y_pred_clf_DecisionTree)
0.7948
La idea básica del bagging es utilizar el conjunto de entrenamiento original para generar centenares o miles de conjuntos similares usando muestreo con reemplazo. En este concepto está basado el algoritmo Random Forest, la combinación de varios árboles de decisión, cada uno entrenado con una realización diferente de los datos. La decisión final del clasificador combinado (el Random Forest) se toma por mayoría, dando el mismo peso a todas las decisiones parciales tomadas por los clasificadores base (los árboles).
clf_RndForest = RandomForestClassifier(n_estimators=20,max_depth=5)
print_cv_results (cross_val_score(clf_RndForest, X_train, y_train))
Mean performance metric = 0.886 SDT of the metric = 0.016 Outcomes by cv fold Fold 1 0.878 Fold 2 0.868 Fold 3 0.912 Fold 4 0.876 Fold 5 0.898
clf_RndForest.fit(X_train,y_train)
y_pred_clf_RndForest = clf_RndForest.predict(X_test)
accuracy_score(y_true=y_test, y_pred=y_pred_clf_RndForest)
0.8924
Sí, los resultados de predicción obtenidos utilizando la técnica del bagging aplicada en el algoritmo Random Forest, basándonos en la precisión (accuracy) como medida de calidad, son mejores que los resultados obtenidos utilizando un árbol de decisión regresor simple.
Esta situación era de esperar, debido a que la técnica del bagging entrena a un conjunto de árboles con diferentes submuestras de datos de la muestra total de datos con la que se entrena al árbol de decisión simple, esto permite que en dichas submuestras sea más sencillo encontrar algunos patrones menos casuales que están presentes en los datos pero que en la muestra total resulta más difícil para un modelo detectarlos (al tener una causalidad pequeña frente al total de la muestra), y sin embargo en estas submuestras al tener un número menor de casos totales, resulta más sencilla la detección de dichos patrones para los diferentes árboles de decisión que componen el bosque aleatorio utilizado.
Una ventaja del bagging usado en el Random Forest es que cada uno de los árboles de decisión ha sido entrenado con una combinación diferente de los datos (muestreo con reemplazo), es decir, cada uno de los árboles no ha visto una determinada parte de los datos originales. Esto define una especie de conjunto de test para cada uno de los árboles, llamado out-of-bag, que puede ser usado para estimar el error del modelo sin necesidad de usar el conjunto de test real que creamos previamente, ni de usar estrategias de cross-validation.
clf_RndForest = RandomForestClassifier(n_estimators=20,max_depth=5, oob_score=True)
clf_RndForest.fit(X_train, y_train)
clf_RndForest.oob_score_
/home/fran/.local/lib/python3.8/site-packages/sklearn/ensemble/_forest.py:541: UserWarning: Some inputs do not have OOB scores. This probably means too few trees were used to compute any reliable oob estimates. /home/fran/.local/lib/python3.8/site-packages/sklearn/ensemble/_forest.py:545: RuntimeWarning: invalid value encountered in true_divide
0.854
La precisión obtenida en el out-of-bag ha resultado algo menor que la obtenida con los métodos de validación anteriores, aunque dicha diferencia no es grande.
Obtener una precisión menor al validar con el conjunto de test out-of-bag era de esperar, ya que es cuando los diferentes árboles de decisión que utiliza el bosque aleatorio, tienen que resolver la problemática de extrapolación, es decir, realizar predicciones de conjuntos de datos con los que no han sido entrenados, y esto resulta más difícil para los modelos, y por lo tanto la precisión obtenida es menor.
En los ejercicios anteriores hemos combinado 20 clasificadores simples en nuestro clasificador combinado. ¿Será que la precisión del clasificador combinado aumenta indefinidamente su desempeño si añadimos más clasificadores?
Para responder a esta pregunta vamos a representar una curva de validación. La curva de validación es una representación gráfica del desempeño de un modelo variando uno de sus parámetros. Esto nos permite entender cuál es el impacto de un determinado parámetro en el desempeño de un modelo.
n_estimators=[20, 50, 100, 150, 200]
train_scores, test_scores = validation_curve(
RandomForestClassifier(), X_train, y_train, param_name='n_estimators',
param_range=n_estimators)
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
plt.xlabel("n_stimators")
plt.ylabel("accuracy")
plt.grid()
plt.fill_between(n_estimators, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.1,
color="r")
plt.fill_between(n_estimators, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.1,
color="g")
plt.plot(n_estimators, train_scores_mean, 'o-', color="r",
label="Training score")
plt.plot(n_estimators, test_scores_mean, 'o-', color="g",
label="Cross-validation score")
plt.legend(loc="best")
plt.show()
Tal y como se puede ver en la gráfica generada, el valor de la precisión va aumentando a medida que aumenta el número de estimadores (árboles combinados utilizados), pero hasta un límite que parece ser 100 según la gráfica.
Podemos ver que el valor de la precisión obtenida, a medida que aumenta el número de estimadores, al poner a prueba el modelo con los datos con los que ha sido entrenados (línea roja de la gráfica anterior) se mantiene en 1, lo que indica que el modelo de regresión obtenido logra resolver a la perfección el problema de interpolación para cualquier número de estimadores.
Pero sin embargo, si observamos el valor de la precisión obtenida al poner a prueba el modelo realizando una validación cruzada, vemos que dicho valor aumenta hasta alcanzar 100 en el número de estimadores, después comienza a bajar en los casos de 150 y 200. Esto es debido a que a medida que aumenta el número de estimadores el modelo tiende a sobreentrenarse, lo que quiere decir que el modelo aprende por completo lo que ocurre en el conjunto de datos con el que se entrena, pero sin embargo al recibir datos nuevos, es decir, al tener que resolver la problemática de extrapolación, no logra realizar las predicciones correctamente, ya que tiende a asociarlo de manera excesiva con la información con la que dicho modelo ha sido entrenado.
En el sistema de Boosting se combinan varios clasificadores débiles sequencialmente, y en cada uno de ellos se da más peso a los datos que han sido erróneamente clasificados en las combinaciones anteriores, para que se concentre así en los casos más difíciles de resolver.
clf_GradientBoosting = GradientBoostingClassifier(max_depth=5, n_estimators=20)
print_cv_results (cross_val_score(clf_GradientBoosting, X_train, y_train, n_jobs=-1))
Mean performance metric = 0.923 SDT of the metric = 0.016 Outcomes by cv fold Fold 1 0.934 Fold 2 0.906 Fold 3 0.948 Fold 4 0.908 Fold 5 0.918
clf_GradientBoosting.fit(X_train,y_train)
y_pred_clf_GradientBoosting = clf_GradientBoosting.predict(X_test)
accuracy_score(y_true=y_test, y_pred=y_pred_clf_GradientBoosting)
0.9192
Basándonos en la precisión (accuracy) como medida de calidad, los resultados de utilizar la técnica de boosting aplicada al algoritmo de Random Forest con respecto a la utilización de un simple árbol de decisión, han mejorado notoriamente, pues hemos pasado de obtener una precisión de 0.79 a una precisión de 0.92.
Esto era de esperar, ya que la técnica de boosting, tal y como se indica anterioremente, da un mayor peso a aquellos datos que han sido clasificados erróneamente anteriormente, lo que permite no volver a cometer dichos errores en los futuros clasificadores y que por tanto la capacidad de predicción del modelo mejore.
En algunos casos este aumento no es muy notorio ya que el algoritmo del árbol de decisión es lo suficiente potente y no es posible mejorarlo, pero en este caso si ha sido posible mejorar el mismo.
Para poder hacer combinación secuencial de modelos, necessitamos tener varios modelos diferentes entrenados.
En nuestro caso, ya tenemos un árbol de decisión, el random forest y el gradient boosting. Vamos a entrenar un par de modelos más.
clf_KNN = KNeighborsClassifier(n_neighbors=2)
clf_KNN.fit(X_train,y_train)
y_pred_clf_KNN = clf_KNN.predict(X_test)
accuracy_score(y_true=y_test, y_pred=y_pred_clf_KNN)
0.9212
clf_SVM = SVC(gamma=0.07)
clf_SVM.fit(X_train,y_train)
y_pred_clf_SVM = clf_SVM.predict(X_test)
accuracy_score(y_true=y_test, y_pred=y_pred_clf_SVM)
0.2
Los resultados obtenidos con el algoritmo de KNN son bastante buenos, con una precisión de 0.92, lo que indica que este está logrando predecir correctamente el 92% de los datos de prueba.
Por el contrario, los resultados obtenidos con el algoritmo de SVM son bastante malos, obteniendo una precisión de 0.2, lo que indica que este está logrando predecir correctamente tan sólo el 20% de los datos de prueba.
Un clasificador de stacking usa como atributos las predicciones hechas por otros clasificadores en lugar de los datos originales de entrada.
attributes = np.column_stack((y_pred_clf_DecisionTree, y_pred_clf_RndForest, y_pred_clf_GradientBoosting,
y_pred_clf_KNN, y_pred_clf_SVM))
enc = OneHotEncoder()
enc.fit(attributes)
attributes_one_hot = enc.transform(attributes)
clf_stack = GradientBoostingClassifier(max_depth=5, n_estimators=20)
print_cv_results (cross_val_score(clf_stack, attributes_one_hot, y_test, n_jobs=-1))
Mean performance metric = 0.929 SDT of the metric = 0.015 Outcomes by cv fold Fold 1 0.926 Fold 2 0.906 Fold 3 0.952 Fold 4 0.938 Fold 5 0.924
Si comparamos los resultados obtenidos de aplicar el Stacking con el clasificador GradientBoosting entrenado anteriormente, que es el clasificador que ha mejores resultados de predicción ha dado, podemos decir que hemos mejorado la precisión, pero mínimamente, pues hemos pasado de tener 0.922 de precisión y una desviación estándar de 0.014 en el GradientBoosting anterior, y en este, aplicando la técnica de Stacking hemos conseguido obtener 0.927 y 0.010 de desviación estándar, lo que refleja que hemos mejorado la precisión y la desviación estándar, pero muy poco, debido a que el método GradientBoosting anterior ya daba muy buenos resultados y mejorarlo de manera realmente notoria era muy complicado.
Sin embargo si lo comparamos con otros clasificadores como puede ser el árbol de decisión o el modelo de SVM sobre todo, vemos que al combinar estos modelos con los demás se obtienen muy buenos resultados.
El caso de cascading es parecido al de stacking pero utilizando no solamente las predicciones parciales de los clasificadores base, sino también los datos originales.
X_test
array([[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]], dtype=uint8)
attributes_cascading = np.column_stack((attributes, X_test))
enc = OneHotEncoder()
enc.fit(attributes_cascading)
attributes_cascading_one_hot = enc.transform(attributes_cascading)
clf_cascading = GradientBoostingClassifier(max_depth=5, n_estimators=20)
print_cv_results (cross_val_score(clf_cascading, attributes_cascading_one_hot, y_test, n_jobs=-1))
Mean performance metric = 0.944 SDT of the metric = 0.011 Outcomes by cv fold Fold 1 0.948 Fold 2 0.938 Fold 3 0.962 Fold 4 0.944 Fold 5 0.928
Aplicando la técnica de Cascading, se ha conseguido mejorar la precisión de los modelos de los que este se compone, incluso del GradientBoosting de una manera más notoria que aplicando la técnica de Stacking, pues hemos pasado de tener una precisión de 0.922 y una desviación estándar de 0.014 a tener una precisión de 0.942 y una desviación estándar de 0.011, es decir, hemos pasado de predecir correctamente un 92% de los datos de media al aplicar cross_validation, a predecir un 94%.
Si además de con el modelo de GradientBoosting, comparamos el modelo obtenido con otros modelos más simples de los que se compone, como puede ser SVM o el árbol de decisión, la mejora es muy notoria.